مقدمة إلى Qiskit
في هذا الكتيب سنستكشف كيف يمكننا برمجة البوابات الكمية والدوائر الكمية باستخدام Qiskit، وكذلك كيف يمكننا تنفيذها على أجهزة محاكاة وحواسيب كمية حقيقية باستخدام أنماط Qiskit. لاحقاً سنتناول طرقاً مختلفة لترميز المعلومات، وسننهي بمثال إضافي على النقل الكمي.
قبل البدء
اتبع تعليمات التثبيت والإعداد إن لم تكن قد فعلت ذلك بعد، بما في ذلك خطوات الإعداد لاستخدام منصة IBM Quantum™.
يُوصى باستخدام بيئة التطوير Jupyter للتفاعل مع الحواسيب الكمية. تأكد من تثبيت دعم التصور الإضافي الموصى به ('qiskit[visualization]'). ستحتاج أيضاً إلى حزمة matplotlib في الجزء الثاني من هذا المثال.
للتعرف على الحوسبة الكمية بشكل عام، قم بزيارة دورة أساسيات المعلومات الكمية في IBM Quantum Learning.
الاستيرادات
# Added by doQumentation — required packages for this notebook
!pip install -q matplotlib numpy qiskit qiskit-aer qiskit-ibm-runtime
# Import necessary modules for this notebook
import time
import qiskit
from qiskit import QuantumCircuit
from qiskit.quantum_info import Statevector
from qiskit.visualization import plot_bloch_multivector, plot_state_qsphere
from qiskit_aer import AerSimulator
from qiskit.quantum_info import SparsePauliOp
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager
from qiskit_ibm_runtime import EstimatorV2 as Estimator
from qiskit_ibm_runtime import SamplerV2 as Sampler
from qiskit_ibm_runtime import QiskitRuntimeService
from qiskit.visualization import plot_histogram
print(qiskit.__version__)
2.3.1
لتنفيذ الدوائر الكمية على الأجهزة الفعلية، تحتاج أولاً إلى إعداد حسابك. يمكنك القيام بذلك على النحو التالي:
- انتقل إلى منصة IBM Quantum® المُحدَّثة.
- انتقل إلى الزاوية اليمنى العلوية (كما هو موضح في الصورة أعلاه)، أنشئ رمز API الخاص بك وانسخه إلى موقع آمن.
- في الخلية التالية، استبدل
deleteThisAndPasteYourAPIKeyHereبمفتاح API الخاص بك. - انتقل إلى الزاوية اليسرى السفلى (كما هو موضح في الصورة أعلاه) وأنشئ نسختك. تأكد من اختيار الخطة المفتوحة.
- بعد إنشاء النسخة، انسخ رمز CRN المرتبط بها. قد تحتاج إلى التحديث لرؤية النسخة.
- في الخلية أدناه، استبدل
deleteThisAndPasteYourCRNHereبرمز CRN الخاص بك.
راجع هذا الدليل للمزيد من التفاصيل حول كيفية إعداد حساب IBM Cloud® الخاص بك.
⚠️ ملاحظة: تعامل مع مفتاح API الخاص بك كما تتعامل مع كلمة مرور آمنة. راجع دليل إعداد السحابة للحصول على مزيد من المعلومات حول استخدام مفتاح API في البيئات الآمنة وغير الموثوقة.
#your_api_key = "deleteThisAndPasteYourAPIKeyHere"
#your_crn = "deleteThisAndPasteYourCRNHere"
QiskitRuntimeService.save_account(
channel="ibm_quantum_platform",
token=your_api_key,
instance=your_crn,
overwrite=True
)
1. البوابات الكمية والدوائر الكمية
الدوائر الكمية هي نماذج للحوسبة الكمية حيث تكون العملية الحساب ية سلسلة من البوابات الكمية. لنلقِ نظرة على بعض البوابات الكمية الشائعة.
بوابة X
تعادل بوابة X دورانًا حول محور X لكرة Bloch بمقدار راديان. تُحوّل إلى و إلى . وهي المكافئ الكمي لبوابة NOT في الحواسيب الكلاسيكية وتُسمى أحياناً قلب البت.
# Let's apply an X-gate on a |0> qubit
qc = QuantumCircuit(1)
qc.x(0)
qc.draw(output='mpl')
# Let's see Bloch sphere visualization
sv = Statevector(qc)
plot_bloch_multivector(sv)

بوابة H
تمثل بوابة هادامار دورانًا بمقدار حول المحور الواقع في منتصف المسافة بين محور ومحور .
تُحوّل حالة الأساس إل ى ، مما يعني أن القياس سيعطي احتمالات متساوية بين 1 أو 0، مما يُنشئ "التراكب" للحالات. وتُكتب هذه الحالة أيضاً بالصيغة .
# Let's apply an H-gate on a |0> qubit
qc = QuantumCircuit(1)
qc.x(0)
qc.h(0)
qc.draw(output='mpl')
# Let's see Bloch sphere visualization
sv = Statevector(qc)
plot_bloch_multivector(sv)

بوابة CX (بوابة CNOT)
تعمل بوابة NOT المتحكم بها (أو CNOT أو CX) على Qubitين. تُنفّذ عملية NOT (المكافئة لتطبيق بوابة X) على الـ Qubit الثاني فقط عندما يكون الـ Qubit الأول في الحالة ، وإلا تتركه دون تغيير. ملاحظة: يرقّم Qiskit البتات في السلسلة من اليمين إلى اليسار.
# Let's apply a CX-gate on |11>
qc = QuantumCircuit(2)
qc.x(0)
qc.x(1)
qc.cx(0,1)
qc.draw(output='mpl')
sv=Statevector(qc)
plot_state_qsphere(sv)

إنشاء حالة Bell الأولى
# Create a Bell state circuit
qc = QuantumCircuit(2)
qc.h(0)
qc.cx(0,1)
# Draw the circuit
qc.draw("mpl")
# Plot the state using q-sphere visualization
sv = Statevector(qc)
plot_state_qsphere(sv)
# q-sphere is useful for visualizing states when Bloch sphere fails to

إنشاء حالة Bell الثانية
# Create a circuit with the second Bell state
qc = QuantumCircuit(2)
qc.x(0)
qc.h(0)
qc.cx(0,1)
qc.draw("mpl")
التفسير هو:
# Get the statevector of the circuit
sv = Statevector(qc)
# Plot the state using qsphere visualization
plot_state_qsphere(sv)

إنشاء حالة GHZ ذات 3 Qubits
# Create a circuit with 3-qubit GHZ state
qc= QuantumCircuit(3)
qc.h(0)
qc.cx(0,1)
qc.cx(0,2)
qc.draw("mpl")
# Get the statevector of the circuit
sv = Statevector(qc)
# Plot the state using qsphere visualization
plot_state_qsphere(sv)

إنشاء حالة شعار Qiskit
# Create a circuit with the Qiskit logo state
qc = QuantumCircuit(4)
qc.h(0)
qc.cx(0,1)
qc.cx(0,2)
qc.cx(0,3)
qc.x(1)
# Draw the circuit
qc.draw("mpl")
# Get the statevector of the circuit
sv = Statevector(qc)
# Plot the state using qsphere visualization
plot_state_qsphere(sv)

2. إنشاء وتشغيل برنامج كمي بسيط
الخطوات الأربع لكتابة برنامج كمي باستخدام أنماط Qiskit هي:
-
تعيين المسألة إلى تنسيق أصيل كمياً.
-
تحسين الدوائر والمؤثرات.
-
التنفيذ باستخدام دالة أولية كمية.
-
تحليل النتائج.
2.1 تعيين المشكلة إلى تنسيق أصيل للحوسبة الكمومية
في البرنامج الكمومي، تُعدّ الدوائر الكمومية (Quantum Circuits) التنسيق الأصيل لتمثيل التعليمات الكمومية، فيما تمثّل المؤثرات (operators) المقادير الرصدية المراد قياسها. عند إنشاء Circuit، تُنشئ عادةً كائنًا جديدًا من نوع QuantumCircuit، ثم تُضيف إليه التعليمات بالتتابع.
تُنشئ خلية الشفرة التالية Circuit تُنتج حالة GHZ، وهي حالة تتشابك فيها ثلاثة Qubits تشابكًا كاملًا مع بعضها.
يستخدم Qiskit SDK ترقيم البت LSb 0، حيث تحمل الخانة القيمة أو . لمزيد من التفاصيل، راجع موضوع ترتيب البتات في Qiskit SDK.
# Create a GHZ state circuit
qc = QuantumCircuit(3)
qc.h(0)
qc.cx(0,1)
qc.cx(0,2)
# Draw the circuit
qc.draw("mpl")
راجع QuantumCircuit في الوثائق للاطلاع على جميع العمليات المتاحة.
عند إنشاء الدوائر الكمومية، يجب مراعاة نوع البيانات المطلوب إرجاعها بعد التنفيذ. يوفر Qiskit طريقتين لإرجاع البيانات: يمكنك الحصول على توزيع احتمالي لمجموعة من Qubits تختارها للقياس، أو الحصول على قيمة التوقع لمقدار رصدي. جهّز حمل العمل لقياس Circuit بإحدى هاتين الطريقتين باستخدام Qiskit primitives (موضحة بالتفصيل في الخطوة 3).
يقيس هذا المثال قيم التوقع باستخدام الوحدة الفرعية qiskit.quantum_info، المحددة عبر المؤثرات (الكائنات الرياضية المستخدمة لتمثيل إجراء أو عملية تغير حالة كمومية). تُنشئ خلية الشفرة التالية ستة مؤثرات باولي ثلاثية Qubits: ZZZ وZZX وZII وXXI وZZI وIII.
# Set up six different observables.
observables_labels = ["ZZZ", "ZZX", "ZII", "XXI", "ZZI", "III"]
observables = [SparsePauliOp(label) for label in observables_labels]
print(observables)
[SparsePauliOp(['ZZZ'],
coeffs=[1.+0.j]), SparsePauliOp(['ZZX'],
coeffs=[1.+0.j]), SparsePauliOp(['ZII'],
coeffs=[1.+0.j]), SparsePauliOp(['XXI'],
coeffs=[1.+0.j]), SparsePauliOp(['ZZI'],
coeffs=[1.+0.j]), SparsePauliOp(['III'],
coeffs=[1.+0.j])]
هنا، المؤثر ZZI مثلًا هو اختصار للضرب التنسيقي ، أي قياس Z على Qubit 2 وZ على Qubit 1 معًا، واست خراج معلومات حول الترابط بين Qubit 2 وQubit 1. تُكتب قيم التوقع كهذه أيضًا على النحو .
إذا كانت الحالة المرصودة هي حالة GHZ الثلاثية Qubits، فيجب أن يساوي قياس القيمة 1.
2.2 تحسين الدوائر والمؤثرات
عند تنفيذ الدوائر على جهاز، من المهم تحسين مجموعة التعليمات التي تحتويها Circuit وتقليل العمق الإجمالي (أي عدد التعليمات تقريبًا) قدر الإمكان. يضمن ذلك الحصول على أفضل النتائج بتقليل تأثيرات الأخطاء والضوضاء. علاوة على ذلك، يجب أن تمتثل تعليمات Circuit لـ بنية مجموعة التعليمات (ISA) الخاصة بجهاز Backend، وأن تراعي بوابات الأساس وترابط Qubits في الجهاز.
تُنشئ الشفرة التالية جهازًا حقيقيًا لإرسال مهمة إليه، وتحوّل Circuit والمقادير الرصدية لتتوافق مع ISA ذلك Backend. إذا لم تحفظ بيانات اعتمادك مسبقًا، اتبع التعليمات هنا للمصادقة باستخدام رمز API الخاص بك.
# Choose a real backend
service = QiskitRuntimeService(channel='ibm_quantum_platform',)
backend = service.least_busy(min_num_qubits=156)
# print backend details
print(
f"Name: {backend.name}\n"
f"Version: {backend.backend_version}\n"
f"No. of qubits: {backend.num_qubits}\n"
f"Processor type: {backend.processor_type}\n"
)
Name: ibm_marrakesh
Version: 1.0.21
No. of qubits: 156
Processor type: {'family': 'Heron', 'revision': '2'}
# option to use the AerSimulator instead of a real quantum device
seed_sim=42
backend=AerSimulator.from_backend(backend,seed_simulator=seed_sim)
تحويل Circuit إلى دائرة ISA
# Convert to an ISA circuit and layout-mapped observables.
pm = generate_preset_pass_manager(backend=backend, optimization_level=2)
isa_circuit = pm.run(qc)
isa_circuit.draw("mpl", idle_wires=False)

mapped_observables = [
observable.apply_layout(isa_circuit.layout) for observable in observables
]
print(mapped_observables)
[SparsePauliOp(['IIIIIIIIIIIIIIIIZIIIIIZIIIIIIIIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII'],
coeffs=[1.+0.j]), SparsePauliOp(['IIIIIIIIIIIIIIIIZIIIIIXIIIIIIIIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII'],
coeffs=[1.+0.j]), SparsePauliOp(['IIIIIIIIIIIIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII'],
coeffs=[1.+0.j]), SparsePauliOp(['IIIIIIIIIIIIIIIIXIIIIIIIIIIIIIIIIIIXIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII'],
coeffs=[1.+0.j]), SparsePauliOp(['IIIIIIIIIIIIIIIIZIIIIIIIIIIIIIIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII'],
coeffs=[1.+0.j]), SparsePauliOp(['IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII'],
coeffs=[1.+0.j])]
2.3 التنفيذ باستخدام Primitives الكمومية
يمكن للحواسيب الكمومية أن تُنتج نتائج عشوائية، لذا تجمع عادةً عينة من المخرجات بتشغيل Circuit مرات عديدة. يمكنك تقدير قيمة المقدار الرصدي باستخدام صنف Estimator. يُعدّ Estimator واحدًا من اثنين من primitives؛ الآخر هو Sampler الذي يمكن استخدامه للحصول على بيانات من حاسوب كمومي. تمتلك هذه الكائنات أسلوب run() الذي ينفّذ مجموعة الدوائر والمقادير الرصدية والمعاملات (إن وُجدت)، باستخدام كتلة PUB الموحدة (PUB).
عند تشغيل هذه الشفرة على عتاد ك مومي حقيقي، ضع في اعتبارك تطبيق تقنيات تخفيف الأخطاء وإزالتها لتقليل الضوضاء الجوهرية للحاسوب الكمومي.
# Construct the Estimator instance.
estimator = Estimator(mode=backend)
estimator.options.resilience_level = 1
estimator.options.default_shots = 5000
إرسال مهمة باستخدام Primitive الـ Estimator.
# One pub, with one circuit to run against six different observables.
job = estimator.run([(isa_circuit, mapped_observables)])
# Use the job ID to retrieve your job data later
print(f">>> Job ID: {job.job_id()}")
>>> Job ID: 97ecd036-1767-49b0-a1dc-c71638c3c3c4
/Users/jma/miniconda3/envs/3122/lib/python3.12/site-packages/qiskit_ibm_runtime/fake_provider/local_service.py:187: UserWarning: The resilience_level option has no effect in local testing mode.
warnings.warn("The resilience_level option has no effect in local testing mode.")
بعد إرسال المهمة، يمكنك الانتظار حتى اكتمالها ضمن نسخة Python الحالية، أو استخدام job_id لاسترداد البيانات لاحقًا. (راجع قسم استرداد المهام للتفاصيل.)
بعد اكتمال المهمة، افحص مخرجاتها من خلال الخاصية result() للمهمة.
# This is the result of the entire submission. You submitted one Pub,
# so this contains one inner result (and some metadata of its own).
job_result = job.result()
# This is the result from our single pub, which had six observables,
# so contains information on all six.
pub_result = job.result()[0]
يمكننا الآن تنفيذ Circuit باستخدام Primitive الـ Sampler أيضًا
# We include the measurements in the circuit
qc.measure_all()
sampler = Sampler(mode=backend)
qc.draw(output="mpl")

إرسال مهمة باستخدام Primitive الـ Sampler.
job_sampler = sampler.run(pm.run([qc]))
# Use the job ID to retrieve your job data later
print(f">>> Job ID: {job_sampler.job_id()}")
# Get the results
results_sampler = job_sampler.result()
>>> Job ID: a6ee4d2f-c80d-4a86-9a76-e4b1a74502e7
2.4 تحليل النتائج
تتضمن خطوة التحليل عادةً المعالجة اللاحقة للنتائج باستخدام، على سبيل المثال، تخفيف أخطاء القياس أو استقراء الضوضاء الصفري (ZNE). يمكنك إدخال هذه النتائج في سير عمل آخر لمزيد من التحليل أو إعداد رسم بياني للقيم والبيانات الرئيسية. بشكل عام، هذه الخطوة خاصة بمشكلتك. في هذا المثال، ارسم كل قيم التوقع التي قِيست لـ Circuit الخاصة بنا.
يمكن الوصول إلى قيم التوقع والانحرافات المعيارية للمقادير الرصدية التي حددتها لـ Estimator من خلال الخاصيتين PubResult.data.evs وPubResult.data.stds لنتيجة المهمة. للحصول على النتائج من Sampler، استخدم الدالة PubResult.data.meas.get_counts()، التي ستُرجع قاموسًا dict من القياسات على شكل سلاسل بتية كمفاتيح وأعداد القياسات كقيم مقابلة لها. لمزيد من المعلومات، راجع البدء مع Sampler.
# Plot the result
from matplotlib import pyplot as plt
values = pub_result.data.evs
errors = pub_result.data.stds
# plotting graph
# Plotting with error bars
plt.errorbar(observables_labels, values, yerr=errors, fmt='-o', capsize=5)
plt.xlabel("Observables")
plt.ylabel("Values")
plt.title("Plot of Observables vs Values with Error Bars")
plt.grid(True)
plt.tight_layout()
plt.show()

نلاحظ أن المقدارين الرصديين و لهما قيمة توقع تساوي 1، إذ يُدخل إشارتين سالبتين تتلاشيان، بينما يعمل كمحايد لا يغير حالة GHZ. أما بقية المقادير الرصدية فلها قيمة توقع تساوي 0، لأن مؤثرات فيها تُدخل عددًا فرديًا من الإشارات السالبة، أو لأن مؤثرات تقلب عددًا من Qubits يجعل الحالات المتداخلة متعامدة.
نرسم الآن نتائج الـ Sampler
counts_list = results_sampler[0].data.meas.get_counts()
print(counts_list)
print(f"Outcomes : {counts_list}")
display(plot_histogram(counts_list, title="GHZ state"))
{'111': 480, '000': 503, '101': 8, '100': 9, '001': 3, '011': 6, '010': 10, '110': 5}
Outcomes : {'111': 480, '000': 503, '101': 8, '100': 9, '001': 3, '011': 6, '010': 10, '110': 5}

2.5 التوسع إلى أعداد كبيرة من Qubits
في الحوسبة الكمومية، يُعدّ العمل على نطاق الفائدة أمرًا بالغ الأهمية لتحقيق تقدم في هذا المجال. يتطلب ذلك إجراء حسابات على نطاق أوسع بكثير؛ مع دوائر قد تستخدم أكثر من 100 Qubit وأكثر من 1000 Gate. يخطو هذا المثال خطوة صغيرة في هذا الاتجاه بتوسيع نطاق مسألة GHZ إلى Qubits. يستخدم سير عمل Qiskit patterns وينتهي بقياس قيمة التوقع .
الخطوة 1. تعيين المشكلة
اكتب دالة تُرجع QuantumCircuit تُهيئ حالة GHZ ذات Qubits (وهي بالأساس حالة بيل موسّعة)، ثم استخدم تلك الدالة لإعداد حالة GHZ من 10 Qubits وجمع المقادير الرصدية المراد قياسها.
def get_qc_for_n_qubit_GHZ_state(n: int) -> QuantumCircuit:
qc = QuantumCircuit(n)
qc.h(0)
for i in range(n-1):
qc.cx(i, i+1)
return qc
n = 10
qc_n_GHZ = get_qc_for_n_qubit_GHZ_state(n)
qc_n_GHZ.draw("mpl")

بعد ذلك، عيّن إلى المؤثرات ذات الاهتمام. يستخدم هذا المثال مؤثرات ZZ بين Qubits لدراسة السلوك عند ازدياد المسافة بينها. ستكشف قيم التوقع غير الدقيقة (الفاسدة) بين Qubits البعيدة عن مستوى الضوضاء الموجودة.
# ZZII...II, ZIZI...II, ... , ZIII...IZ
operator_strings = [
"Z" + i * "I" + "Z" + "I" * (n-i-2) for i in range(n-1)
]
print(operator_strings)
print(len(operator_strings))
operators = [SparsePauliOp(operator) for operator in operator_strings]
['ZZIIIIIIII', 'ZIZIIIIIII', 'ZIIZIIIIII', 'ZIIIZIIIII', 'ZIIIIZIIII', 'ZIIIIIZIII', 'ZIIIIIIZII', 'ZIIIIIIIZI', 'ZIIIIIIIIZ']
9
الخطوة 2. تحسين المشكلة للتنفيذ على Backend الكمومي
حوّل Circuit والمقادير الرصدية لتتوافق مع ISA الخاص بـ Backend.
# Convert to an ISA circuit and layout-mapped observables.
pm = generate_preset_pass_manager(backend=backend, optimization_level=2)
isa_circuit = pm.run(qc_n_GHZ)
isa_operators_list = [operator.apply_layout(isa_circuit.layout) for operator in operators]
الخطوة 3. التنفيذ على Backend
أرسل المهمة وإذا نفّذتها على عتاد حقيقي فعّل إزالة الأخطاء باستخدام تقنية تُعرف بـ الفصل الديناميكي (dynamical decoupling). يحدد مستوى المرونة مقدار المقاومة المبنية ضد الأخطاء. تُولّد المستويات الأعلى نتائج أكثر دقة، لكن على حساب وقت معالجة أطول. لمزيد من الشرح حول الخيارات المحددة في الشفرة التالية، راجع تكوين تخفيف الأخطاء لـ Qiskit Runtime.
# Submit the circuit to Estimator
job = estimator.run([(isa_circuit, isa_operators_list)])
job_id = job.job_id()
/Users/jma/miniconda3/envs/3122/lib/python3.12/site-packages/qiskit_ibm_runtime/fake_provider/local_service.py:187: UserWarning: The resilience_level option has no effect in local testing mode.
warnings.warn("The resilience_level option has no effect in local testing mode.")
الخطوة 4. معالجة النتائج لاحقًا
لفهم سلوك الحالات الكمومية المتشابكة على العتاد الحقيقي، نحلل الترابطات الزوجية بين Qubits في الأساس Z. نُركّز تحديدًا على قيم التوقع ⟨Z₀Zᵢ⟩ التي تقيس مدى الترابط بين Qubit 0 وكل Qubit i آخر. سنرسم تحديدًا:
ما القيم التي تتوقع رؤيتها في الرسم البياني لـ ؟
الخيارات:
أ) تناقصية كلما زاد
ب) ثابتة عند 1
ج) انحرافات صغيرة حول 1
د) تتناوب بين 1 و0 لقيم الفردية والزوجية
data = list(range(1, len(operators) + 1)) # Distance between the Z operators
result = job.result()[0]
values = result.data.evs # Expectation value at each Z operator.
values = [
v / values[0] for v in values
] # Normalize the expectation values to evaluate how they decay with distance.
plt.plot(data, values, marker="o", label=f"{n}-qubit GHZ state")
plt.xlabel("Distance between qubits $i$")
plt.ylabel(r"$\langle Z_i Z_0 \rangle / \langle Z_1 Z_0 \rangle $")
plt.legend()
plt.show()

في هذا الرسم البياني نلاحظ أن يتذبذب حول القيمة 1، على الرغم من أنه في المحاكاة المثالية يجب أن يساوي جميع القيمة 1.
كما ترى، نتائج تجارب الـ 10 Qubits جيدة لكن لا تزال تحتوي على بعض الأخطاء. إحدى طرق تحسين النتائج هي تنفيذ حالة GHZ بكفاءة أعلى.
عادةً ما تُنفَّذ حالة GHZ بتسلسل متدرج من بوابات CNOT. غير أنه يمكن تنفيذ حالة GHZ بكفاءة أكبر، بتقليل العمق ثنائي Qubits من n إلى n/2 أو أقل.
أحد المقاييس الهامة لمعرفة مدى دقة النتائج أو مدى انخفاض الضوضاء في Circuit هو عمق البوابات ثنائية Qubits. ذلك لأن معدلات الخطأ للبوابات ثنائية Qubits (أعلى بـ ~10 أضعاف من البوابات أحادية Qubit) تهيمن على أخطاء الدائرة بأكملها. استخدم الشفرة التالية للحصول على عمق البوابات ثنائية Qubits لـ Circuit.
qc.depth(lambda x: x.operation.num_qubits == 2)
def better_ghz(n):
"fan out"
s = int(n / 2)
qc = QuantumCircuit(n)
qc.h(s)
for m in range(s, 0, -1):
qc.cx(m, m - 1)
if not (n % 2 == 0 and m == s):
qc.cx(n - m - 1, n - m)
return qc
better_ghz(n).draw("mpl")

# Check 2-qubit gate depth before transpilation
qc_better_ghz = better_ghz(n)
qc_better_ghz.depth(lambda x: x.operation.num_qubits == 2)
5
من الأمور اللافتة للنظر هنا أننا تمكنا من تقليل العمق الكمومي للدائرة التي نريد تنفيذها، فقط بالتفكير بذكاء وإيجاد طريقة مختلفة لبرمجتها. غير أنه في بعض المواقف والخوارزميات لن نستطيع الاعتماد على هذه الحيل الذكية. هنا يأتي دور الـ Transpiler، إذ يساعدنا على تحسين جميع هذه الجوانب بكفاءة حتى لا نضطر إلى القلق كثيرًا بشأنها.
3. ترميز المعلومات
3.1 الترميز بالسعة
بعد أن رأينا كيفية بناء الدوائر الكمومية، يُثير الاهتمامَ استكشافُ كيفية ترميز المعلومات الكلاسيكية في الحالات الكمومية. إحدى الطرق القوية هي الترميز بالسعة، حيث تمثل سعات (amplitudes) الحالة الكمومية مكونات متجه كلاسيكي.
لنأخذ مثالًا بسيطًا. افترض أننا نريد ترميز المتجه الكلاسيكي
في حالة كمومية من Qubitين. الهدف هو إعداد الحالة الكمومية: